// Copyright (c) 2020-present, INSPUR Co, Ltd. All rights reserved.
// This source code is licensed under Apache 2.0 License.

#pragma once

#include <atomic>
#include <list>

#include "thread_safe_dlink.h"
#include "epoche.h"
#include "mvcc_key.h"
#include "db/dbformat.h"
#include "pure_mem/rowex/art_tree.h"
#include "pure_mem/rangearena/thread_safe_queue.h"
#include "rocksdb/slice.h"
#include "version_node.h"

namespace rocksdb {
// ARTRef object pointer is stored as ART leaf node.
// ART tree just index user key, no HLC or sequence number.
// ARTRef object contains multi version values for one user key.
class ARTRef {
 public:
  ARTRef() {
    version_head_.store(nullptr);
    next_.store(nullptr);
    prev_.store(nullptr);
  }
  ARTRef* Next() { return next_.load(); }
  ARTRef* Prev() { return prev_.load(); }

  VersionNode* getContentList() { return this->version_head_.load(); }

  bool CASContentList(VersionNode* expected, VersionNode* x) {
    return version_head_.compare_exchange_strong(expected, x);
  }
  void setContentList(VersionNode* x) {
    return version_head_.store(x);
  }

  void setNext(ARTRef* x) { return next_.store(x); }

  void setPrev(ARTRef* x) { return prev_.store(x); }

  bool CASNext(ARTRef* expected, ARTRef* x) {
    return next_.compare_exchange_strong(expected, x);
  }

  bool CASPrev(ARTRef* expected, ARTRef* x) {
    return prev_.compare_exchange_strong(expected, x);
  }
  static int CompareTo(ARTRef* other) {
    // cannot use this function.
    assert(false);
    return 0;
  }

 private:
  // double link list, store multi version values of current key.
  std::atomic<VersionNode*> version_head_;
  // double link list of ARTRef, each ARTRef node need store prev and next
  // pointer.
  std::atomic<ARTRef*> prev_;
  std::atomic<ARTRef*> next_;
};

// ARTIndex4Mem class used to encapsulate ART tree operations.
class ARTIndex4Mem {
 public:
  ARTIndex4Mem();
  ~ARTIndex4Mem();

  // no allow copy.
  ARTIndex4Mem(const ARTIndex4Mem&) = delete;
  ARTIndex4Mem(ARTIndex4Mem&& t) = delete;
  ARTIndex4Mem& operator=(const ARTIndex4Mem&) = delete;
  ARTIndex4Mem& operator=(ARTIndex4Mem&&) = delete;

  // get userkey corresponeding ARTRef object in ART tree,
  // if not exist new one object and put onto ART tree.
  ARTRef* getOrNewARTRef(Slice& userKey, VersionNode* node);
  // get one ARTRef object whose key equal or greater userkey,
  // while no object match, return nullptr.
  ARTRef* getEGARTRef(Slice& userKey) const;

  ARTRef* head() const { return artref_dlink_->getHead(); }
  ARTRef* tail() const { return artref_dlink_->getTail(); }

 private:
  // ART tree object pointer, build when ARTIndex4Mem constructing.
  art_rowex::Tree* tree_;
  // ARTRef object double link list. used for key range scan.
  ConcurrentDLink<ARTRef*>* artref_dlink_;
};

}  // namespace rocksdb